iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 28
0
Modern Web

D3.js新手開發基本圖表系列 第 28

Day28 練習 - 直方圖 (1)

  • 分享至 

  • xImage
  •  

一、條直方圖(Column Histogram)繪製

**直方圖 (Histogram Chart)長條圖 (Line Chart)**有些微的不同,在「Day09 練習題準備 - 圖表類型的理解」中曾經討論過圖表的分類。長條圖的資料點是離散的,而直方圖是連續的資料形式,每一個點代表的是一個區間的資料,可以用長條、或者線(較多資料點時適用)的方式來呈現。

首先準備一組直方圖的資料,用d3.random.normal()產生隨機的常態分佈資料。

var rand = d3.random.normal(170, 10)

var dataSet = []

for(var i=0; i<100; i++){
    dataSet.push(rand())
}

加入svg

var width = 500
var height = 380
var padding = {top:30, right:30, bottom:30, left:30}

var svg = d3.select("body").append("svg")
  .attr("width",width)
  .attr("height",height)

D3也提供了直方圖的layout,語法為d3.layout.histogram()

histogram.range([min, max]) 設定資料分佈的範圍
histogram.bins(count) 設定長條圖個數
histogram.frequency(bool) true為數量、false為機率

var binNum = 10, rangeMin = 130, rangeMax = 210

var histogram = d3.layout.histogram()
  .range([rangeMin, rangeMax])
  .bins(binNum)
  .frequency(true)
  
var hisData = histogram(dataSet)

把hisData印出來後可看到資料結構如下圖。

其中x為資料區間的下限值、dx為區間的長度、y為落到此區間的數量。

https://ithelp.ithome.com.tw/upload/images/20181111/20096057yjeW1upgUg.png

接著要建立比例尺,由於x軸的資料為離散的,所以要使用的是序數比例尺(Ordinal Scale),而y軸要使用的是線性比例尺(Linear Scale)

在前面的文章已多次練習過線性比例尺(可參考Day13、Day14、Day16文章)。

序數比例尺使用方式有些許不同,序數比例尺的定義域以及值域都是離散的,在定義域放入剛才以直方圖layout轉換出來的x;而值域放入座標值。

其中值域這邊使用的方法是ordinal.ordinalRoundBands([min, max])rangeRoundBandsrangeBands類似,都是可以放入資料區間(如使用range則需放入所有的各別資料),而rangeRoundBands的差別是會將結果取整數。

var xAxisWidth = width - padding.left - padding.right
var yAxisWidth = 450
var xTicks = hisData.map(function(d){ return d.x }) // 直方圖layout轉換出來的資料中的x

var xScale = d3.scale.ordinal() // 序數比例尺
  .domain(xTicks) // 定義域
  .rangeRoundBands([0, xAxisWidth]) // 值域

var yScale = d3.scale.linear() // 線性比例尺
  .domain([d3.min(hisData, function(d){ return d.y }), d3.max(hisData, function(d){ return d.y })]) // 定義域
  .range([5, yAxisWidth]) // 值域

接著開始繪製x軸刻度

var xAxis = d3.svg.axis()
  .scale(xScale)
  .orient("bottom")

svg.append("g")
  .attr("class", "xAxis")
  .attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
  .call(xAxis)

以svg元素<rect>繪製直方圖。

其中data()放入的就是layout所轉換出來的資料,設定屬性xyheight由layout資料中的x、y資料使用比例尺函式做計算。

而width屬性使用比例尺所提供的rangeBand()函式取得每一段的寬度。

var gRect = svg.append("g")
  .attr("transform", "translate(" + padding.left + "," + (-padding.bottom) + ")")
  
gRect.selectAll("rect")
  .data(hisData)
  .enter()
  .append("rect")
  .attr("class", "rect")
  .attr("x", function(d,i){
      return xScale(d.x)
  })
  .attr("y", function(d,i){
      return height - yScale(d.y)
  })
  .attr("width", function(d,i){
      return xScale.rangeBand()
  })
  .attr("height", function(d){
      return yScale(d.y)
  })

結果如下圖。

https://ithelp.ithome.com.tw/upload/images/20181111/20096057BYT0MMM00h.png

二、序數比例尺(Ordinal Scale)間距設定

序數比例尺中的rangeRoundBands可以選擇加入第2及第3個參數,第2個參數是所有區間之間的間距(padding)、第3個參數是單獨設定最左和最右兩邊的間距。

間距需輸入0~1之間的數值,單位為區間寬度的倍數。比如說,間距設為0.1時間距寬度即為區間寬度的1/10,如設為1、則間距和區間寬度會相等(區間會不見)。

以下修改原程式碼,將間距設為0.1、左右兩邊的間距設為0.3。

var xScale = d3.scale.ordinal()
  .domain(xTicks)
  .rangeRoundBands([0, xAxisWidth],0.1,0.3)

結果如下圖。

https://ithelp.ithome.com.tw/upload/images/20181111/20096057mrSzSb8wFA.png

線上檢視連結可參考:http://jsfiddle.net/upstairs0102/dnfye7mv/


上一篇
Day27 互動
下一篇
Day29 練習 - 直方圖 (2)
系列文
D3.js新手開發基本圖表30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言